﻿using BoboBrowse.Api;
using BoboBrowse.Facets;
using BoboBrowse.Facets.impl;
using Lucene.Net.Analysis;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using Lucene.Net.Store;
using NewsMine.DomainObjects;
using System.Collections.Generic;
using System.Linq;
using System;
using System.IO;
using Lucene.Net.Documents;

namespace NewsDiscoveryEngine.Index
{
   

    public class FacetReader
    {
        static Dictionary<string, List<categoryRank>> overallKeywordStats = null;


        double minUniversalPercentage = 20;
        public List<categoryRank> GetAnalyticalStatsForString(string inputString, out Dictionary<string, List<categoryRank>> inDepthDetailList)
        {
            string configuredMinValue = NewsMine.Configuration.ConfigurationManager.Get("keywordMatchingMinValue");
            if (!string.IsNullOrEmpty(configuredMinValue))
            {
                minUniversalPercentage = Convert.ToInt32(configuredMinValue);
            }

            inDepthDetailList = new Dictionary<string, List<categoryRank>>();

            if (string.IsNullOrEmpty(inputString))
                return null;

            Dictionary<string, List<categoryRank>> results;

            if (overallKeywordStats != null)
            {
                results = overallKeywordStats;
            }
            else if (File.Exists("C:\\keyworkStats.dat"))
            {
                results = (Dictionary<string, List<categoryRank>>)NewsMine.Utilities.FileExetensions.DeserializeFromFile<Dictionary<string, List<categoryRank>>>("C:\\keyworkStats.dat");
            }
            else
            {
                results = GetKeyWordDensityCloudForAllCategories();

                NewsMine.Utilities.FileExetensions.SerializeObjectToFileUsingProtoBuff("C:\\keyworkStats.dat", results);
            }

            overallKeywordStats = results;

            inputString = NewsMine.Utilities.TempAnalyzer.CleanString(inputString);
            inputString = NewsMine.Utilities.TempAnalyzer.RemoveNoiceWordsFromString(inputString);

            inputString = inputString.ToLower();

            List<string> tokens = inputString.Split(' ').ToList();

            if (tokens == null || tokens.Count == 0)
                return null;
            tokens = tokens.Where(t=> !string.IsNullOrEmpty(t)).ToList().Select(t => t.ToLower()).ToList();

            tokens = tokens.Distinct().ToList();

            var matchedKeyWordRanks = new Dictionary<string, List<categoryRank>>();

            var matchedKeyWordList = new List<categoryRank>();

            foreach (var token in tokens)
            {
                if (results.ContainsKey(token))
                {
                    var keywordInfoForToken = results[token];

                    if (keywordInfoForToken == null || keywordInfoForToken.Count == 0)
                        continue;
                    
                    keywordInfoForToken = keywordInfoForToken.Where(k => k.GlobalPercent >= minUniversalPercentage).ToList();

                    matchedKeyWordRanks.Add(token, keywordInfoForToken);

                    matchedKeyWordList.AddRange(keywordInfoForToken);
                }
            }

            inDepthDetailList = matchedKeyWordRanks;

            List<categoryRank> statList = matchedKeyWordList.GroupBy(x => x.Name).Select(g => new categoryRank { Name = g.Key, GlobalPercent = g.Sum(x => x.GlobalPercent) }).ToList<categoryRank>();

            statList = statList.OrderBy(o => o.GlobalPercent >= o.GlobalPercent).ToList();

            return statList;

           // return matchedKeyWordList;
           
        }



        public Dictionary<string, List<categoryRank>> GetKeyWordDensityCloudForAllCategories(string fieldName = "titlelink", string contextQuery = "*:*")
        {
            
            List<string> categoryNames = NewsMine.DataManagers.KeyWordManager.GetAllCategories();

            Dictionary<string, List<KeyWordTokenDensity>> dictCategorizedKeywords = 
                new Dictionary<string, List<KeyWordTokenDensity>>();

             List<KeyWordTokenDensity>  allKeywordDensityList = GetKeyWordDensityCloud("*:*", fieldName, contextQuery);

             allKeywordDensityList = allKeywordDensityList.OrderBy(d => d.TokenUniversalCount >= d.TokenUniversalCount).ToList();
              
            // AllKeywordsDictionary

            Dictionary<string, List<categoryRank>> resultedKeyWordCategoryScores = new Dictionary<string, List<categoryRank>>();

            foreach (var key in allKeywordDensityList)
            {
                resultedKeyWordCategoryScores.Add(key.TokenName,new List<categoryRank>{ new categoryRank { Name="*",Count=key.TokenCount,GlobalPercent = key.TokenUniversalPercentage }});
            }

            foreach (var cat in categoryNames)
            {
                dictCategorizedKeywords.Add(cat, GetKeyWordDensityCloudByCategoryName(cat,fieldName, contextQuery));
            }

            foreach (var dict in dictCategorizedKeywords)
            {
                var tempKeyWordDensityList = dict.Value;

                foreach (var kd in tempKeyWordDensityList)
                {
                    resultedKeyWordCategoryScores[kd.TokenName].Add(new categoryRank { Name = dict.Key.ToLower(), Count = kd.TokenCount, GlobalPercent = kd.TokenUniversalPercentage });
                }
            }

            overallKeywordStats = resultedKeyWordCategoryScores;
            //try to the intersection and find all the records
            return resultedKeyWordCategoryScores;
        }

        public List<KeyWordTokenDensity> GetKeyWordDensityCloudByCategoryName(string categoryName, string fieldName = "titlelink", string contextQuery = "*:*")
        {
            if (string.IsNullOrEmpty(categoryName))
                throw new ApplicationException("Invalid categoryName");

            var keywords = NewsMine.DataManagers.KeyWordManager.GetAllKeywordsByCategory(categoryName);

            string query = "titlelink:" + String.Join(" OR titlelink:", keywords);

            return GetKeyWordDensityCloud(query, fieldName, contextQuery);
        }

        public List<KeyWordTokenDensity> GetKeyWordDensityCloudByTimeContext(string searchText, string fieldName, int numberPreviousHours,IndexReader reader=null)
        {
            string contextQuery = GetTimeLimitQuery(DateTime.UtcNow.AddHours(-1 * numberPreviousHours), DateTime.UtcNow, "pubdate");

            return GetKeyWordDensityCloud(searchText, fieldName, contextQuery);
        }

        public List<KeyWordTokenDensity> GetKeyWordDensityCloud(string searchText, string fieldName, string contextQuery = "*:*",IndexReader reader=null)
        {
            if (string.IsNullOrEmpty(searchText) || string.IsNullOrEmpty(fieldName))
            {
                throw new ApplicationException("you need to provide proper query and search text.");
            }

            string searchQueryAlongWithContextQuery = "( " + searchText + ") AND " + contextQuery;

            int maxCountContextual = -1;
            int maxCountUniversal = -1;
            int maxCountForSearch = -1;


            FacetReader facetReader = new FacetReader();

            Dictionary<string, int> facetsUniversal = facetReader.GetFacets("*:*", fieldName, out maxCountUniversal,reader);

            Dictionary<string, int> facetsContextual = null;

            if (contextQuery == "*:*")
            {
                facetsContextual = facetsUniversal;
                maxCountContextual = maxCountUniversal;
            }
            else
            {
                facetsContextual = facetReader.GetFacets(contextQuery, fieldName, out maxCountContextual,reader);
            }

            Dictionary<string, int> facets = null;

            if (searchQueryAlongWithContextQuery == "*:*")
            {
                facets = facetsUniversal;
                maxCountForSearch = maxCountUniversal;
            }
            else
            {
                facets = facetReader.GetFacets(searchQueryAlongWithContextQuery, fieldName, out maxCountForSearch,reader);
            }

            if (maxCountContextual == 0)
            {
                maxCountContextual = -1;
            }
            if (maxCountUniversal == 0)
                maxCountUniversal = -1;
            if (maxCountForSearch == 0)
                maxCountForSearch = -1;


            double totalPercentOfFilteredDocs = maxCountForSearch * 100 / maxCountContextual;

            int precesion = 10000;

            List<KeyWordTokenDensity> densityInfoList = new List<KeyWordTokenDensity>();

          

            foreach (var item in facets)
            {
                densityInfoList.Add(
                    new KeyWordTokenDensity
                    {
                        TokenName = item.Key,
                        TokenCount = Convert.ToDouble(item.Value),
                        TokenCountPercentage = Convert.ToDouble((item.Value * precesion / maxCountForSearch)) /100,
                        ContextualTokenCount = Convert.ToDouble(facetsContextual[item.Key]),
                        ContextualTokenPercentage = Convert.ToDouble((item.Value * precesion / facetsContextual[item.Key])) / 100,
                        TokenUniversalCount = Convert.ToDouble(facetsUniversal[item.Key]),
                        TokenUniversalPercentage = Convert.ToDouble((item.Value * precesion / facetsUniversal[item.Key])) / 100
                    }
                    );
            }

           


            return densityInfoList;
        }

        public Dictionary<string, int> GetFacets(string inputSearchQuery, string fieldName, out int maxDocCount, IndexReader reader =null)
        {
            if (string.IsNullOrEmpty(inputSearchQuery))
            {
                throw new ApplicationException("search text is must");
            }

            if (string.IsNullOrEmpty(fieldName) || string.IsNullOrEmpty(fieldName))
            {
                throw new ApplicationException("fieldName is must");
            }



            if (reader == null)
                reader = GetIndexReader();

            // Get the maximum document count for the given query.
            IndexSearcher searcher = new IndexSearcher(reader);
            QueryParser _queryParser = new QueryParser("id", new WhitespaceAnalyzer());
            Query query = _queryParser.Parse(inputSearchQuery);
            Hits CountHits = searcher.Search(query);
            maxDocCount = CountHits.Length();

            FacetHandler facetHandler = new MultiValueFacetHandler(fieldName);
            ICollection<FacetHandler> handlerList = new FacetHandler[] { facetHandler };

            // decorate it with a bobo index reader
            BoboIndexReader boboReader = BoboIndexReader.GetInstance(reader, handlerList);

            // creating a browse request
            BrowseRequest browseRequest = new BrowseRequest();
            browseRequest.Count = 100;
            browseRequest.Offset = 0;
            browseRequest.FetchStoredFields = true;

            // parse a query
            QueryParser parser = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "Entity", new WhitespaceAnalyzer());
            Query q = parser.Parse(inputSearchQuery);
            browseRequest.Query = q;

            // add the facet output specs
            FacetSpec facetSpec = new FacetSpec();
            facetSpec.OrderBy = FacetSpec.FacetSortSpec.OrderHitsDesc;
            //facetSpec.MinHitCount = 5;
            browseRequest.SetFacetSpec(fieldName, facetSpec);

            //FacetSpec categorySpec = new FacetSpec();
            //categorySpec.MinHitCount = 2;
            //categorySpec.OrderBy = FacetSpec.FacetSortSpec.OrderHitsDesc;


            // perform browse
            IBrowsable browser = new BoboBrowse.Api.BoboBrowser(boboReader);

            BrowseResult result = browser.Browse(browseRequest);

            // Showing results now
            int totalHits = result.NumHits;
            BrowseHit[] hits = result.Hits;

            Dictionary<String, IFacetAccessible> facetMap = result.FacetMap;

            IFacetAccessible colorFacets = facetMap[fieldName];

            IEnumerable<BrowseFacet> facetVals = colorFacets.GetFacets();

            Dictionary<string, int> resultFacetDictionary = new Dictionary<string, int>();

            foreach (BrowseFacet facet in facetVals)
            {
                resultFacetDictionary.Add(facet.Value.ToString(), facet.HitCount);
            }

            //reader.Close();

            return resultFacetDictionary;
        }

        public Dictionary<string, int> GetKeywordVsDomainCountFacets(string inputSearchQuery, string fieldName)
        {
            if (string.IsNullOrEmpty(inputSearchQuery))
            {
                throw new ApplicationException("search text is must");
            }

            if (string.IsNullOrEmpty(fieldName))
            {
                throw new ApplicationException("fieldName is must field here");
            }
          
            IndexReader reader = GetIndexReader();

            FacetHandler facetHandler = new MultiValueFacetHandler(fieldName);
            ICollection<FacetHandler> handlerList = new FacetHandler[] { facetHandler };

            // decorate it with a bobo index reader
            BoboIndexReader boboReader = BoboIndexReader.GetInstance(reader, handlerList);

            // creating a browse request
            BrowseRequest browseRequest = new BrowseRequest();
            browseRequest.Count = 100;
            browseRequest.Offset = 0;
            browseRequest.FetchStoredFields = true;

            // parse a query
            QueryParser parser = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "Entity", new WhitespaceAnalyzer());
            Query q = parser.Parse(inputSearchQuery);
            browseRequest.Query = q;

            // add the facet output specs
            FacetSpec facetSpec = new FacetSpec();
            facetSpec.OrderBy = FacetSpec.FacetSortSpec.OrderHitsDesc;
            //facetSpec.MinHitCount = 5;
            browseRequest.SetFacetSpec(fieldName, facetSpec);

            //FacetSpec categorySpec = new FacetSpec();
            //categorySpec.MinHitCount = 2;
            //categorySpec.OrderBy = FacetSpec.FacetSortSpec.OrderHitsDesc;


            // perform browse
            IBrowsable browser = new BoboBrowse.Api.BoboBrowser(boboReader);

            BrowseResult result = browser.Browse(browseRequest);

            // Showing results now
            int totalHits = result.NumHits;
            BrowseHit[] hits = result.Hits;

            Dictionary<String, IFacetAccessible> facetMap = result.FacetMap;

            IFacetAccessible colorFacets = facetMap[fieldName];

            IEnumerable<BrowseFacet> facetVals = colorFacets.GetFacets();

            Dictionary<string, int> resultFacetDictionary = new Dictionary<string, int>();

            foreach (BrowseFacet facet in facetVals)
            {
                resultFacetDictionary.Add(facet.Value.ToString(), facet.HitCount);
            }

            reader.Close();

            return resultFacetDictionary;
        }

        public static IndexReader GetIndexReader()
        {
            string indexDir = System.IO.Path.Combine(NewsMine.Configuration.ConfigurationManager.StorageBaseFolder, @"Indexes\HtmlFeeds");

            Lucene.Net.Store.Directory idx = FSDirectory.Open(new System.IO.DirectoryInfo(indexDir));
            IndexReader reader = IndexReader.Open(idx, true);
            return reader;
        }

        public static string GetTimeLimitQuery( DateTime fromDate, DateTime toDate,string fieldName = "pubdate")
        {
            return string.Format("pubdate:[{0} TO {1}]", DateTools.DateToString(fromDate, DateTools.Resolution.HOUR),
                DateTools.DateToString(toDate, DateTools.Resolution.HOUR));
        }
    }
}
